# Rectnagle geometry scaling
#
# Copyright (C) 2010 Yotam Medini <yotam.medini@gmail.com>
#
# This file is part of Gupicasa.
#
# Gupicasa is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Gupicasa is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Gupicasa.  If not, see <http://www.gnu.org/licenses/>.


import arith
import lru
import debug


def scale_to_fit(wh, wh_max):
    (retw, reth) = wh_max
    # If we ignire aspect ratio, then we scale dimension i:
    # wh_max[i] / wh[i].
    # In order to fit, we pick the smaller scale, as is.
    # Now:   wh_max[0]/wh[0] < wh_max[1]/wh[1]
    # iff    wh_max[0]*wh[1] < wh_max[1]*wh[0]
    if wh_max[0]*wh[1] < wh_max[1]*wh[0]:
        reth = (wh[1] * wh_max[0]) / wh[0]
    else:
        retw = (wh[0] * wh_max[1]) / wh[1]
    return (retw, reth)


class Mapper:


    def __init__(self, target_min, target_max, cache_size=16):
        self.target_min = target_min
        self.target_max = target_max
        self.cache = lru.Cache(cache_size)


    def wh_map(self, wh):
        to_wh = self.cache.get(wh)
        if to_wh is None:
            (w, h) = (wh[0], wh[1])
            to_wh = self.tiny_geometry(w, h, self.target_min, self.target_max)
            self.cache.set(wh, to_wh)
        return to_wh


    def tiny_geometry(self, w, h, tmin, tmax):
        debug.log("%d %d %d %d" % (w, h, tmin, tmax))
        gcd_wh = arith.gcd(w, h)
        ds = arith.divisors(gcd_wh)
        maxwh = max(w, h)
        good_divs = filter(lambda d: tmin <= maxwh/d <= tmax, ds)
        good_divs.sort()
        gd = []
        if len(good_divs) > 0:
            tinyw = w / good_divs[-1]
            tinyh = h / good_divs[-1]
        elif maxwh < tmax:
            tinyw = w
            tinyh = h
        else:
            d_min = maxwh / tmax
            d_max = maxwh / tmin
            ds = range(d_min, d_max + 1)
            d_gcd = map(lambda d: (arith.gcd(d, maxwh), d), ds)
            d_gcd.sort()
            debug.log("maxwh=%d, tmin=%d d_gcd=%s" % (maxwh, tmin, d_gcd))
            best_d = d_gcd[-1][1]
            tinyw = (w + best_d/2) / best_d
            tinyh = (h + best_d/2) / best_d
        return (tinyw, tinyh)


if __name__ == '__main__':
    import random
    import sys
    import string

    opt = sys.argv[1]
    if opt == 'testmapper':
        fn = sys.argv[2]
        f = sys.stdin if fn == '-' else open(fn, 'r')
        line = f.readline()
        (tmin, tmax, csize) = t = tuple(map(lambda s: int(s),
                                            string.split(line)[:3]))
        sys.stderr.write("tmin=%d, tmax=%d, csize=%d\n" % t)
        mapper = Mapper(tmin, tmax, csize)
        line = f.readline()
        while line != '':
            wh = tuple(map(lambda s: int(s), string.split(line)[:2]))
            to_wh = mapper.wh_map(wh)
            sys.stdout.write("wh=%s --> to_wh=%s\n" % (wh, to_wh))
            line = f.readline()
        if fn != '-':
            f.close()
        sys.stdout.write("Cache: queries=%d, misses=%d\n" %
                         (mapper.cache.queries, mapper.cache.misses))
        sys.exit(0)

    elif opt == 'testfit':
        n = map(lambda s: int(s), sys.argv[2:2+4])
        wh = (n[0], n[1])
        wh_max = (n[2], n[3])
        rwh = scale_to_fit(wh, wh_max)
        sys.stdout.write("wh=%s, wh_max=%s, rwh=%s\n" % (wh, wh_max, rwh))

    elif opt == 'testtiny':
        n = map(lambda s: int(s), sys.argv[2:2+4])
        wh = (n[0], n[1])
        wh_max = (n[2], n[3])
        mapper = Mapper(n[2], n[3])
        to_wh = mapper.wh_map(wh)
        sys.stdout.write("wh=%s, wh_max=%s, to_wh=%s\n" % (wh, wh_max, to_wh))

    else:
        sys.stderr.write("Unknown test\n")
        sys.exit(1)
